/* HImode div/mod functions for the GCC support library for the Renesas RL78 processors.
   Copyright (C) 2012-2022 Free Software Foundation, Inc.
   Contributed by Red Hat.

   This file is part of GCC.

   GCC is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   GCC is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   Under Section 7 of GPL version 3, you are granted additional
   permissions described in the GCC Runtime Library Exception, version
   3.1, as published by the Free Software Foundation.

   You should have received a copy of the GNU General Public License and
   a copy of the GCC Runtime Library Exception along with this program;
   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
   <http://www.gnu.org/licenses/>.  */

#include "vregs.h"

#if defined __RL78_MUL_G14__

START_FUNC ___divhi3
	;; r8 = 4[sp] / 6[sp]

	;; Test for a negative denumerator.
	movw	ax, [sp+6]
	mov1	cy, a.7
	movw	de, ax
	bc	$__div_neg_den

	;; Test for a negative numerator.
	movw	ax, [sp+4]
	mov1	cy, a.7
	bc	$__div_neg_num

	;; Neither are negative - we can use the unsigned divide instruction.
__div_no_convert:	
	push	psw
	di
	divhu
	pop	psw
	
	movw	r8, ax
	ret

__div_neg_den:
	;; Negate the denumerator (which is in DE)
	clrw	ax
	subw	ax, de
	movw	de, ax
	
	;; Test for a negative numerator.
	movw	ax, [sp+4]
	mov1	cy, a.7
	;; If it is not negative then we perform the division and then negate the result.
	bnc	$__div_then_convert

	;; Otherwise we negate the numerator and then go with an unsigned division.
	movw	bc, ax
	clrw	ax
	subw	ax, bc
	br	$__div_no_convert

__div_neg_num:
	;; Negate the numerator (which is in AX)
	;; We know that the denumerator is positive.
	movw	bc, ax
	clrw	ax
	subw	ax, bc
	
__div_then_convert:
	push	psw
	di
	divhu
	pop	psw
	
	;; Negate result and transfer into r8
	movw	bc, ax
	clrw	ax
	subw	ax, bc
	movw	r8, ax
	ret

END_FUNC ___divhi3

;----------------------------------------------------------------------

START_FUNC ___modhi3
	;; r8 = 4[sp] % 6[sp]

	;; Test for a negative denumerator.
	movw	ax, [sp+6]
	mov1	cy, a.7
	movw	de, ax
	bc	$__mod_neg_den

	;; Test for a negative numerator.
	movw	ax, [sp+4]
	mov1	cy, a.7
	bc	$__mod_neg_num

	;; Neither are negative - we can use the unsigned divide instruction.
__mod_no_convert:	
	push	psw
	di
	divhu
	pop	psw

	movw	ax, de
	movw	r8, ax
	ret

__mod_neg_den:	
	;; Negate the denumerator (which is in DE)
	clrw	ax
	subw	ax, de
	movw	de, ax
	
	;; Test for a negative numerator.
	movw	ax, [sp+4]
	mov1	cy, a.7
	;; If it is not negative then we perform the modulo operation without conversion.
	bnc	$__mod_no_convert

	;; Otherwise we negate the numerator and then go with an unsigned modulo operation.
	movw	bc, ax
	clrw	ax
	subw	ax, bc
	br	$__mod_then_convert

__mod_neg_num:
	;; Negate the numerator (which is in AX)
	;; We know that the denumerator is positive.
	movw	bc, ax
	clrw	ax
	subw	ax, bc
	
__mod_then_convert:
	push	psw
	di
	divhu
	pop	psw

	;; Negate result and transfer into r8
	clrw	  ax
	subw	  ax, de
	movw	  r8, ax
	ret

END_FUNC ___modhi3

;----------------------------------------------------------------------

#elif defined __RL78_MUL_G13__

	;; The G13 S2 core does not have a 16 bit divide peripheral.
	;; So instead we perform a 32-bit divide and twiddle the inputs
	;; as necessary.

	;; Hardware registers.  Note - these values match the silicon, not the documentation.
	MDAL = 0xffff0
	MDAH = 0xffff2
	MDBL = 0xffff6
	MDBH = 0xffff4
	MDCL = 0xf00e0
	MDCH = 0xf00e2
	MDUC = 0xf00e8

.macro _Negate src, dest
	movw	ax, !\src
	movw	bc, ax
	clrw	ax
	subw	ax, bc
	movw	\dest, ax
.endm
	
;----------------------------------------------------------------------
	
START_FUNC ___divhi3
	;; r8 = 4[sp] / 6[sp] (signed division)

	mov	a, #0xC0	; Set DIVMODE=1 and MACMODE=1
	mov	!MDUC, a	; This preps the peripheral for division without interrupt generation

	clrw	ax     		; Clear the top 16-bits of the divisor and dividend
	movw	MDBH, ax
	movw	MDAH, ax
	
	;; Load and test for a negative denumerator.
	movw	ax, [sp+6]
	movw	MDBL, ax
	mov1	cy, a.7
	bc	$__div_neg_den

	;; Load and test for a negative numerator.
	movw	ax, [sp+4]
	mov1	cy, a.7
	movw	MDAL, ax
	bc	$__div_neg_num

	;; Neither are negative - we can use the unsigned divide hardware.
__div_no_convert:	
	mov	a, #0xC1	; Set the DIVST bit in MDUC
	mov	!MDUC, a	; This starts the division op

1:	mov	a, !MDUC	; Wait 16 clocks or until DIVST is clear
	bt	a.0, $1b

  	movw	ax, MDAL	; Read the result
	movw	r8, ax
	ret

__div_neg_den:
	;; Negate the denumerator (which is in MDBL)
	_Negate MDBL MDBL

	;; Load and test for a negative numerator.
	movw	ax, [sp+4]
	mov1	cy, a.7
	movw	MDAL, ax
	;; If it is not negative then we perform the division and then negate the result.
	bnc	$__div_then_convert

	;; Otherwise we negate the numerator and then go with a straightforward unsigned division.
	_Negate MDAL MDAL
	br	$!__div_no_convert

__div_neg_num:
	;; Negate the numerator (which is in MDAL)
	;; We know that the denumerator is positive.
	_Negate MDAL MDAL
	
__div_then_convert:
	mov	a, #0xC1	; Set the DIVST bit in MDUC
	mov	!MDUC, a	; This starts the division op

1:	mov	a, !MDUC	; Wait 16 clocks or until DIVST is clear
	bt	a.0, $1b

	;; Negate result and transfer into r8
	_Negate MDAL r8
	ret

END_FUNC ___divhi3

;----------------------------------------------------------------------

START_FUNC ___modhi3
	;; r8 = 4[sp] % 6[sp] (signed modulus)

	mov	a, #0xC0	; Set DIVMODE=1 and MACMODE=1
	mov	!MDUC, a	; This preps the peripheral for division without interrupt generation

	clrw	ax     		; Clear the top 16-bits of the divisor and dividend
	movw	MDBH, ax
	movw	MDAH, ax
	
	;; Load and test for a negative denumerator.
	movw	ax, [sp+6]
	movw	MDBL, ax
	mov1	cy, a.7
	bc	$__mod_neg_den

	;; Load and test for a negative numerator.
	movw	ax, [sp+4]
	mov1	cy, a.7
	movw	MDAL, ax
	bc	$__mod_neg_num

	;; Neither are negative - we can use the unsigned divide hardware
__mod_no_convert:	
	mov	a, #0xC1	; Set the DIVST bit in MDUC
	mov	!MDUC, a	; This starts the division op

1:	mov	a, !MDUC	; Wait 16 clocks or until DIVST is clear
	bt	a.0, $1b

  	movw	ax, !MDCL	; Read the remainder
	movw	r8, ax
	ret

__mod_neg_den:
	;; Negate the denumerator (which is in MDBL)
	_Negate MDBL MDBL
	
	;; Load and test for a negative numerator.
	movw	ax, [sp+4]
	mov1	cy, a.7
	movw	MDAL, ax
	;; If it is not negative then we perform the modulo operation without conversion.
	bnc	$__mod_no_convert

	;; Otherwise we negate the numerator and then go with a modulo followed by negation.
	_Negate MDAL MDAL
	br	$!__mod_then_convert

__mod_neg_num:
	;; Negate the numerator (which is in MDAL)
	;; We know that the denumerator is positive.
	_Negate MDAL MDAL
	
__mod_then_convert:
	mov	a, #0xC1	; Set the DIVST bit in MDUC
	mov	!MDUC, a	; This starts the division op

1:	mov	a, !MDUC	; Wait 16 clocks or until DIVST is clear
	bt	a.0, $1b

	_Negate	MDCL r8
	ret

END_FUNC ___modhi3

;----------------------------------------------------------------------

START_FUNC ___udivhi3
	;; r8 = 4[sp] / 6[sp] (unsigned division)

	mov	a, #0xC0	; Set DIVMODE=1 and MACMODE=1
	mov	!MDUC, a	; This preps the peripheral for division without interrupt generation

	movw	ax, [sp+4]	; Load the divisor
	movw	MDAL, ax
	movw	ax, [sp+6]	; Load the dividend
	movw	MDBL, ax
	clrw	ax
	movw	MDAH, ax
	movw	MDBH, ax
	
	mov	a, #0xC1	; Set the DIVST bit in MDUC
	mov	!MDUC, a	; This starts the division op

1:	mov	a, !MDUC	; Wait 16 clocks or until DIVST is clear
	bt	a.0, $1b

  	movw	ax, !MDAL	; Read the remainder
	movw	r8, ax
	ret

END_FUNC   ___udivhi3

;----------------------------------------------------------------------

START_FUNC ___umodhi3
	;; r8 = 4[sp] % 6[sp] (unsigned modulus)

	mov	a, #0xC0	; Set DIVMODE=1 and MACMODE=1
	mov	!MDUC, a	; This preps the peripheral for division without interrupt generation

	movw	ax, [sp+4]	; Load the divisor
	movw	MDAL, ax
	movw	ax, [sp+6]	; Load the dividend
	movw	MDBL, ax
	clrw	ax
	movw	MDAH, ax
	movw	MDBH, ax
	
	mov	a, #0xC1	; Set the DIVST bit in MDUC
	mov	!MDUC, a	; This starts the division op

1:	mov	a, !MDUC	; Wait 16 clocks or until DIVST is clear
	bt	a.0, $1b

  	movw	ax, !MDCL	; Read the remainder
	movw	r8, ax
	ret
	
END_FUNC   ___umodhi3

;----------------------------------------------------------------------
	
#elif defined __RL78_MUL_NONE__
	
.macro MAKE_GENERIC  which,need_result

	.if \need_result
	quot = r8
	num = r10
	den = r12
	bit = r14
	.else
	num = r8
	quot = r10
	den = r12
	bit = r14
	.endif

	quotB0 = quot
	quotB1 = quot+1
	
	numB0 = num
	numB1 = num+1
	
	denB0 = den
	denB1 = den+1
	
	bitB0 = bit
	bitB1 = bit+1

#define bit	bc
#define bitB0	c
#define bitB1	b

	START_FUNC __generic_hidivmod\which

num_lt_den\which:
	.if \need_result
	movw	r8, #0
	.else
	movw	ax, [sp+8]
	movw	r8, ax
	.endif
	ret

	;; These routines leave DE alone - the signed functions use DE
	;; to store sign information that must remain intact

	.if \need_result
	.global __generic_hidiv
__generic_hidiv:

	.else

	.global __generic_himod
__generic_himod:

	.endif

	;; (quot,rem) = 8[sp] /% 10[sp]

	movw	hl, sp
	movw	ax, [hl+10] ; denH
	cmpw	ax, [hl+8] ; numH
	bh	$num_lt_den\which

	;; (quot,rem) = 16[sp] /% 20[sp]

	;; copy numerator
	movw	ax, [hl+8]
	movw	num, ax

	;; copy denomonator
	movw	ax, [hl+10]
	movw	den, ax

	movw	ax, den
	cmpw	ax, #0
	bnz	$den_not_zero\which
	.if \need_result
	movw    quot, #0
	.else
	movw	num, #0
	.endif
	ret

den_not_zero\which:
	.if \need_result
	;; zero out quot
	movw	quot, #0
	.endif

	;; initialize bit to 1
	movw	bit, #1

; while (den < num && !(den & (1L << BITS_MINUS_1)))

shift_den_bit\which:	
	movw	ax, den
	mov1	cy,a.7
	bc	$enter_main_loop\which
	cmpw	ax, num
	bh	$enter_main_loop\which

	;; den <<= 1
;	movw	ax, den		; already has it from the cmpw above
	shlw	ax, 1
	movw	den, ax

	;; bit <<= 1
	.if \need_result
#ifdef bit
	shlw	bit, 1
#else
	movw	ax, bit
	shlw	ax, 1
	movw	bit, ax
#endif
	.else
	;; if we don't need to compute the quotent, we don't need an
	;; actual bit *mask*, we just need to keep track of which bit
	inc	bitB0
	.endif

	br	$shift_den_bit\which

main_loop\which:

	;; if (num >= den) (cmp den > num)
	movw	ax, den
	cmpw	ax, num
	bh	$next_loop\which

	;; num -= den
	movw	ax, num
	subw	ax, den
	movw	num, ax

	.if \need_result
	;; res |= bit
	mov	a, quotB0
	or	a, bitB0
	mov	quotB0, a
	mov	a, quotB1
	or	a, bitB1
	mov	quotB1, a
	.endif

next_loop\which:	

	;; den >>= 1
	movw	ax, den
	shrw	ax, 1
	movw	den, ax

	.if \need_result
	;; bit >>= 1
	movw	ax, bit
	shrw	ax, 1
	movw	bit, ax
	.else
	dec	bitB0
	.endif

enter_main_loop\which:
	.if \need_result
	movw	ax, bit
	cmpw	ax, #0
	.else
	cmp0	bitB0
	.endif
	bnz	$main_loop\which

main_loop_done\which:	
	ret
	END_FUNC __generic_hidivmod\which
.endm
;----------------------------------------------------------------------

	MAKE_GENERIC _d 1
	MAKE_GENERIC _m 0

;----------------------------------------------------------------------

START_FUNC ___udivhi3
	;; r8 = 4[sp] / 6[sp]
	call	$!__generic_hidiv
	ret
END_FUNC ___udivhi3
	

START_FUNC ___umodhi3
	;; r8 = 4[sp] % 6[sp]
	call	$!__generic_himod
	ret
END_FUNC ___umodhi3

;----------------------------------------------------------------------

.macro NEG_AX
	movw	hl, ax
	movw	ax, #0
	subw	ax, [hl]
	movw	[hl], ax
.endm

;----------------------------------------------------------------------

START_FUNC ___divhi3
	;; r8 = 4[sp] / 6[sp]
	movw	de, #0
	mov	a, [sp+5]
	mov1	cy, a.7
	bc	$div_signed_num
	mov	a, [sp+7]
	mov1	cy, a.7
	bc	$div_signed_den
	call	$!__generic_hidiv
	ret
	
div_signed_num:
	;; neg [sp+4]
	movw	ax, sp
	addw	ax, #4
	NEG_AX
	mov	d, #1
	mov	a, [sp+7]
	mov1	cy, a.7
	bnc	$div_unsigned_den
div_signed_den:	
	;; neg [sp+6]
	movw	ax, sp
	addw	ax, #6
	NEG_AX
	mov	e, #1
div_unsigned_den:	
	call	$!__generic_hidiv

	mov	a, d
	cmp0	a
	bz	$div_skip_restore_num
	;;  We have to restore the numerator [sp+4]
	movw	ax, sp
	addw	ax, #4
	NEG_AX
	mov	a, d
div_skip_restore_num:	
	xor	a, e
	bz	$div_no_neg
	movw	ax, #r8
	NEG_AX
div_no_neg:
	mov	a, e
	cmp0	a
	bz	$div_skip_restore_den
	movw	ax, sp
	addw	ax, #6
	NEG_AX
div_skip_restore_den:	
	ret
END_FUNC ___divhi3
	

START_FUNC ___modhi3
	;; r8 = 4[sp] % 6[sp]
	movw	de, #0
	mov	a, [sp+5]
	mov1	cy, a.7
	bc	$mod_signed_num
	mov	a, [sp+7]
	mov1	cy, a.7
	bc	$mod_signed_den
	call	$!__generic_himod
	ret
	
mod_signed_num:
	;; neg [sp+4]
	movw	ax, sp
	addw	ax, #4
	NEG_AX
	mov	d, #1
	mov	a, [sp+7]
	mov1	cy, a.7
	bnc	$mod_unsigned_den
mod_signed_den:	
	;; neg [sp+6]
	movw	ax, sp
	addw	ax, #6
	NEG_AX
mod_unsigned_den:	
	call	$!__generic_himod

	mov	a, d
	cmp0	a
	bz	$mod_no_neg
	movw	ax, #r8
	NEG_AX
	;;  Also restore numerator
	movw 	ax, sp
	addw	ax, #4
	NEG_AX
mod_no_neg:
	mov	a, e
	cmp0	a
	bz	$mod_skip_restore_den
	movw	ax, sp
	addw	ax, #6
	NEG_AX
mod_skip_restore_den:	
	ret
END_FUNC ___modhi3

;----------------------------------------------------------------------

#else

#error "Unknown RL78 hardware multiply/divide support"

#endif
